#![allow(unreachable_patterns)]

use std::ffi::{CString, CStr};
use libc::c_char;
use std::mem::transmute;

use crate::sys;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Keycode {
    Backspace          = sys::SDLK_BACKSPACE as i32,
    Tab                = sys::SDLK_TAB as i32,
    Return             = sys::SDLK_RETURN as i32,
    Escape             = sys::SDLK_ESCAPE as i32,
    Space              = sys::SDLK_SPACE as i32,
    Exclaim            = sys::SDLK_EXCLAIM as i32,
    Quotedbl           = sys::SDLK_QUOTEDBL as i32,
    Hash               = sys::SDLK_HASH as i32,
    Dollar             = sys::SDLK_DOLLAR as i32,
    Percent            = sys::SDLK_PERCENT as i32,
    Ampersand          = sys::SDLK_AMPERSAND as i32,
    Quote              = sys::SDLK_QUOTE as i32,
    LeftParen          = sys::SDLK_LEFTPAREN as i32,
    RightParen         = sys::SDLK_RIGHTPAREN as i32,
    Asterisk           = sys::SDLK_ASTERISK as i32,
    Plus               = sys::SDLK_PLUS as i32,
    Comma              = sys::SDLK_COMMA as i32,
    Minus              = sys::SDLK_MINUS as i32,
    Period             = sys::SDLK_PERIOD as i32,
    Slash              = sys::SDLK_SLASH as i32,
    Num0               = sys::SDLK_0 as i32,
    Num1               = sys::SDLK_1 as i32,
    Num2               = sys::SDLK_2 as i32,
    Num3               = sys::SDLK_3 as i32,
    Num4               = sys::SDLK_4 as i32,
    Num5               = sys::SDLK_5 as i32,
    Num6               = sys::SDLK_6 as i32,
    Num7               = sys::SDLK_7 as i32,
    Num8               = sys::SDLK_8 as i32,
    Num9               = sys::SDLK_9 as i32,
    Colon              = sys::SDLK_COLON as i32,
    Semicolon          = sys::SDLK_SEMICOLON as i32,
    Less               = sys::SDLK_LESS as i32,
    Equals             = sys::SDLK_EQUALS as i32,
    Greater            = sys::SDLK_GREATER as i32,
    Question           = sys::SDLK_QUESTION as i32,
    At                 = sys::SDLK_AT as i32,
    LeftBracket        = sys::SDLK_LEFTBRACKET as i32,
    Backslash          = sys::SDLK_BACKSLASH as i32,
    RightBracket       = sys::SDLK_RIGHTBRACKET as i32,
    Caret              = sys::SDLK_CARET as i32,
    Underscore         = sys::SDLK_UNDERSCORE as i32,
    Backquote          = sys::SDLK_BACKQUOTE as i32,
    A                  = sys::SDLK_a as i32,
    B                  = sys::SDLK_b as i32,
    C                  = sys::SDLK_c as i32,
    D                  = sys::SDLK_d as i32,
    E                  = sys::SDLK_e as i32,
    F                  = sys::SDLK_f as i32,
    G                  = sys::SDLK_g as i32,
    H                  = sys::SDLK_h as i32,
    I                  = sys::SDLK_i as i32,
    J                  = sys::SDLK_j as i32,
    K                  = sys::SDLK_k as i32,
    L                  = sys::SDLK_l as i32,
    M                  = sys::SDLK_m as i32,
    N                  = sys::SDLK_n as i32,
    O                  = sys::SDLK_o as i32,
    P                  = sys::SDLK_p as i32,
    Q                  = sys::SDLK_q as i32,
    R                  = sys::SDLK_r as i32,
    S                  = sys::SDLK_s as i32,
    T                  = sys::SDLK_t as i32,
    U                  = sys::SDLK_u as i32,
    V                  = sys::SDLK_v as i32,
    W                  = sys::SDLK_w as i32,
    X                  = sys::SDLK_x as i32,
    Y                  = sys::SDLK_y as i32,
    Z                  = sys::SDLK_z as i32,
    Delete             = sys::SDLK_DELETE as i32,
    CapsLock           = sys::SDLK_CAPSLOCK as i32,
    F1                 = sys::SDLK_F1 as i32,
    F2                 = sys::SDLK_F2 as i32,
    F3                 = sys::SDLK_F3 as i32,
    F4                 = sys::SDLK_F4 as i32,
    F5                 = sys::SDLK_F5 as i32,
    F6                 = sys::SDLK_F6 as i32,
    F7                 = sys::SDLK_F7 as i32,
    F8                 = sys::SDLK_F8 as i32,
    F9                 = sys::SDLK_F9 as i32,
    F10                = sys::SDLK_F10 as i32,
    F11                = sys::SDLK_F11 as i32,
    F12                = sys::SDLK_F12 as i32,
    PrintScreen        = sys::SDLK_PRINTSCREEN as i32,
    ScrollLock         = sys::SDLK_SCROLLLOCK as i32,
    Pause              = sys::SDLK_PAUSE as i32,
    Insert             = sys::SDLK_INSERT as i32,
    Home               = sys::SDLK_HOME as i32,
    PageUp             = sys::SDLK_PAGEUP as i32,
    End                = sys::SDLK_END as i32,
    PageDown           = sys::SDLK_PAGEDOWN as i32,
    Right              = sys::SDLK_RIGHT as i32,
    Left               = sys::SDLK_LEFT as i32,
    Down               = sys::SDLK_DOWN as i32,
    Up                 = sys::SDLK_UP as i32,
    NumLockClear       = sys::SDLK_NUMLOCKCLEAR as i32,
    KpDivide           = sys::SDLK_KP_DIVIDE as i32,
    KpMultiply         = sys::SDLK_KP_MULTIPLY as i32,
    KpMinus            = sys::SDLK_KP_MINUS as i32,
    KpPlus             = sys::SDLK_KP_PLUS as i32,
    KpEnter            = sys::SDLK_KP_ENTER as i32,
    Kp1                = sys::SDLK_KP_1 as i32,
    Kp2                = sys::SDLK_KP_2 as i32,
    Kp3                = sys::SDLK_KP_3 as i32,
    Kp4                = sys::SDLK_KP_4 as i32,
    Kp5                = sys::SDLK_KP_5 as i32,
    Kp6                = sys::SDLK_KP_6 as i32,
    Kp7                = sys::SDLK_KP_7 as i32,
    Kp8                = sys::SDLK_KP_8 as i32,
    Kp9                = sys::SDLK_KP_9 as i32,
    Kp0                = sys::SDLK_KP_0 as i32,
    KpPeriod           = sys::SDLK_KP_PERIOD as i32,
    Application        = sys::SDLK_APPLICATION as i32,
    Power              = sys::SDLK_POWER as i32,
    KpEquals           = sys::SDLK_KP_EQUALS as i32,
    F13                = sys::SDLK_F13 as i32,
    F14                = sys::SDLK_F14 as i32,
    F15                = sys::SDLK_F15 as i32,
    F16                = sys::SDLK_F16 as i32,
    F17                = sys::SDLK_F17 as i32,
    F18                = sys::SDLK_F18 as i32,
    F19                = sys::SDLK_F19 as i32,
    F20                = sys::SDLK_F20 as i32,
    F21                = sys::SDLK_F21 as i32,
    F22                = sys::SDLK_F22 as i32,
    F23                = sys::SDLK_F23 as i32,
    F24                = sys::SDLK_F24 as i32,
    Execute            = sys::SDLK_EXECUTE as i32,
    Help               = sys::SDLK_HELP as i32,
    Menu               = sys::SDLK_MENU as i32,
    Select             = sys::SDLK_SELECT as i32,
    Stop               = sys::SDLK_STOP as i32,
    Again              = sys::SDLK_AGAIN as i32,
    Undo               = sys::SDLK_UNDO as i32,
    Cut                = sys::SDLK_CUT as i32,
    Copy               = sys::SDLK_COPY as i32,
    Paste              = sys::SDLK_PASTE as i32,
    Find               = sys::SDLK_FIND as i32,
    Mute               = sys::SDLK_MUTE as i32,
    VolumeUp           = sys::SDLK_VOLUMEUP as i32,
    VolumeDown         = sys::SDLK_VOLUMEDOWN as i32,
    KpComma            = sys::SDLK_KP_COMMA as i32,
    KpEqualsAS400      = sys::SDLK_KP_EQUALSAS400 as i32,
    AltErase           = sys::SDLK_ALTERASE as i32,
    Sysreq             = sys::SDLK_SYSREQ as i32,
    Cancel             = sys::SDLK_CANCEL as i32,
    Clear              = sys::SDLK_CLEAR as i32,
    Prior              = sys::SDLK_PRIOR as i32,
    Return2            = sys::SDLK_RETURN2 as i32,
    Separator          = sys::SDLK_SEPARATOR as i32,
    Out                = sys::SDLK_OUT as i32,
    Oper               = sys::SDLK_OPER as i32,
    ClearAgain         = sys::SDLK_CLEARAGAIN as i32,
    CrSel              = sys::SDLK_CRSEL as i32,
    ExSel              = sys::SDLK_EXSEL as i32,
    Kp00               = sys::SDLK_KP_00 as i32,
    Kp000              = sys::SDLK_KP_000 as i32,
    ThousandsSeparator = sys::SDLK_THOUSANDSSEPARATOR as i32,
    DecimalSeparator   = sys::SDLK_DECIMALSEPARATOR as i32,
    CurrencyUnit       = sys::SDLK_CURRENCYUNIT as i32,
    CurrencySubUnit    = sys::SDLK_CURRENCYSUBUNIT as i32,
    KpLeftParen        = sys::SDLK_KP_LEFTPAREN as i32,
    KpRightParen       = sys::SDLK_KP_RIGHTPAREN as i32,
    KpLeftBrace        = sys::SDLK_KP_LEFTBRACE as i32,
    KpRightBrace       = sys::SDLK_KP_RIGHTBRACE as i32,
    KpTab              = sys::SDLK_KP_TAB as i32,
    KpBackspace        = sys::SDLK_KP_BACKSPACE as i32,
    KpA                = sys::SDLK_KP_A as i32,
    KpB                = sys::SDLK_KP_B as i32,
    KpC                = sys::SDLK_KP_C as i32,
    KpD                = sys::SDLK_KP_D as i32,
    KpE                = sys::SDLK_KP_E as i32,
    KpF                = sys::SDLK_KP_F as i32,
    KpXor              = sys::SDLK_KP_XOR as i32,
    KpPower            = sys::SDLK_KP_POWER as i32,
    KpPercent          = sys::SDLK_KP_PERCENT as i32,
    KpLess             = sys::SDLK_KP_LESS as i32,
    KpGreater          = sys::SDLK_KP_GREATER as i32,
    KpAmpersand        = sys::SDLK_KP_AMPERSAND as i32,
    KpDblAmpersand     = sys::SDLK_KP_DBLAMPERSAND as i32,
    KpVerticalBar      = sys::SDLK_KP_VERTICALBAR as i32,
    KpDblVerticalBar   = sys::SDLK_KP_DBLVERTICALBAR as i32,
    KpColon            = sys::SDLK_KP_COLON as i32,
    KpHash             = sys::SDLK_KP_HASH as i32,
    KpSpace            = sys::SDLK_KP_SPACE as i32,
    KpAt               = sys::SDLK_KP_AT as i32,
    KpExclam           = sys::SDLK_KP_EXCLAM as i32,
    KpMemStore         = sys::SDLK_KP_MEMSTORE as i32,
    KpMemRecall        = sys::SDLK_KP_MEMRECALL as i32,
    KpMemClear         = sys::SDLK_KP_MEMCLEAR as i32,
    KpMemAdd           = sys::SDLK_KP_MEMADD as i32,
    KpMemSubtract      = sys::SDLK_KP_MEMSUBTRACT as i32,
    KpMemMultiply      = sys::SDLK_KP_MEMMULTIPLY as i32,
    KpMemDivide        = sys::SDLK_KP_MEMDIVIDE as i32,
    KpPlusMinus        = sys::SDLK_KP_PLUSMINUS as i32,
    KpClear            = sys::SDLK_KP_CLEAR as i32,
    KpClearEntry       = sys::SDLK_KP_CLEARENTRY as i32,
    KpBinary           = sys::SDLK_KP_BINARY as i32,
    KpOctal            = sys::SDLK_KP_OCTAL as i32,
    KpDecimal          = sys::SDLK_KP_DECIMAL as i32,
    KpHexadecimal      = sys::SDLK_KP_HEXADECIMAL as i32,
    LCtrl              = sys::SDLK_LCTRL as i32,
    LShift             = sys::SDLK_LSHIFT as i32,
    LAlt               = sys::SDLK_LALT as i32,
    LGui               = sys::SDLK_LGUI as i32,
    RCtrl              = sys::SDLK_RCTRL as i32,
    RShift             = sys::SDLK_RSHIFT as i32,
    RAlt               = sys::SDLK_RALT as i32,
    RGui               = sys::SDLK_RGUI as i32,
    Mode               = sys::SDLK_MODE as i32,
    AudioNext          = sys::SDLK_AUDIONEXT as i32,
    AudioPrev          = sys::SDLK_AUDIOPREV as i32,
    AudioStop          = sys::SDLK_AUDIOSTOP as i32,
    AudioPlay          = sys::SDLK_AUDIOPLAY as i32,
    AudioMute          = sys::SDLK_AUDIOMUTE as i32,
    MediaSelect        = sys::SDLK_MEDIASELECT as i32,
    Www                = sys::SDLK_WWW as i32,
    Mail               = sys::SDLK_MAIL as i32,
    Calculator         = sys::SDLK_CALCULATOR as i32,
    Computer           = sys::SDLK_COMPUTER as i32,
    AcSearch           = sys::SDLK_AC_SEARCH as i32,
    AcHome             = sys::SDLK_AC_HOME as i32,
    AcBack             = sys::SDLK_AC_BACK as i32,
    AcForward          = sys::SDLK_AC_FORWARD as i32,
    AcStop             = sys::SDLK_AC_STOP as i32,
    AcRefresh          = sys::SDLK_AC_REFRESH as i32,
    AcBookmarks        = sys::SDLK_AC_BOOKMARKS as i32,
    BrightnessDown     = sys::SDLK_BRIGHTNESSDOWN as i32,
    BrightnessUp       = sys::SDLK_BRIGHTNESSUP as i32,
    DisplaySwitch      = sys::SDLK_DISPLAYSWITCH as i32,
    KbdIllumToggle     = sys::SDLK_KBDILLUMTOGGLE as i32,
    KbdIllumDown       = sys::SDLK_KBDILLUMDOWN as i32,
    KbdIllumUp         = sys::SDLK_KBDILLUMUP as i32,
    Eject              = sys::SDLK_EJECT as i32,
    Sleep              = sys::SDLK_SLEEP as i32,
}

impl Keycode {
    pub fn from_i32(n: i32) -> Option<Keycode> {
        use self::Keycode::*;
        let n = n as u32;

        Some( match unsafe { transmute(n) } {
            sys::SDLK_BACKSPACE           => Backspace,
            sys::SDLK_TAB                 => Tab,
            sys::SDLK_RETURN              => Return,
            sys::SDLK_ESCAPE              => Escape,
            sys::SDLK_SPACE               => Space,
            sys::SDLK_EXCLAIM             => Exclaim,
            sys::SDLK_QUOTEDBL            => Quotedbl,
            sys::SDLK_HASH                => Hash,
            sys::SDLK_DOLLAR              => Dollar,
            sys::SDLK_PERCENT             => Percent,
            sys::SDLK_AMPERSAND           => Ampersand,
            sys::SDLK_QUOTE               => Quote,
            sys::SDLK_LEFTPAREN           => LeftParen,
            sys::SDLK_RIGHTPAREN          => RightParen,
            sys::SDLK_ASTERISK            => Asterisk,
            sys::SDLK_PLUS                => Plus,
            sys::SDLK_COMMA               => Comma,
            sys::SDLK_MINUS               => Minus,
            sys::SDLK_PERIOD              => Period,
            sys::SDLK_SLASH               => Slash,
            sys::SDLK_0                   => Num0,
            sys::SDLK_1                   => Num1,
            sys::SDLK_2                   => Num2,
            sys::SDLK_3                   => Num3,
            sys::SDLK_4                   => Num4,
            sys::SDLK_5                   => Num5,
            sys::SDLK_6                   => Num6,
            sys::SDLK_7                   => Num7,
            sys::SDLK_8                   => Num8,
            sys::SDLK_9                   => Num9,
            sys::SDLK_COLON               => Colon,
            sys::SDLK_SEMICOLON           => Semicolon,
            sys::SDLK_LESS                => Less,
            sys::SDLK_EQUALS              => Equals,
            sys::SDLK_GREATER             => Greater,
            sys::SDLK_QUESTION            => Question,
            sys::SDLK_AT                  => At,
            sys::SDLK_LEFTBRACKET         => LeftBracket,
            sys::SDLK_BACKSLASH           => Backslash,
            sys::SDLK_RIGHTBRACKET        => RightBracket,
            sys::SDLK_CARET               => Caret,
            sys::SDLK_UNDERSCORE          => Underscore,
            sys::SDLK_BACKQUOTE           => Backquote,
            sys::SDLK_a                   => A,
            sys::SDLK_b                   => B,
            sys::SDLK_c                   => C,
            sys::SDLK_d                   => D,
            sys::SDLK_e                   => E,
            sys::SDLK_f                   => F,
            sys::SDLK_g                   => G,
            sys::SDLK_h                   => H,
            sys::SDLK_i                   => I,
            sys::SDLK_j                   => J,
            sys::SDLK_k                   => K,
            sys::SDLK_l                   => L,
            sys::SDLK_m                   => M,
            sys::SDLK_n                   => N,
            sys::SDLK_o                   => O,
            sys::SDLK_p                   => P,
            sys::SDLK_q                   => Q,
            sys::SDLK_r                   => R,
            sys::SDLK_s                   => S,
            sys::SDLK_t                   => T,
            sys::SDLK_u                   => U,
            sys::SDLK_v                   => V,
            sys::SDLK_w                   => W,
            sys::SDLK_x                   => X,
            sys::SDLK_y                   => Y,
            sys::SDLK_z                   => Z,
            sys::SDLK_DELETE              => Delete,
            sys::SDLK_CAPSLOCK            => CapsLock,
            sys::SDLK_F1                  => F1,
            sys::SDLK_F2                  => F2,
            sys::SDLK_F3                  => F3,
            sys::SDLK_F4                  => F4,
            sys::SDLK_F5                  => F5,
            sys::SDLK_F6                  => F6,
            sys::SDLK_F7                  => F7,
            sys::SDLK_F8                  => F8,
            sys::SDLK_F9                  => F9,
            sys::SDLK_F10                 => F10,
            sys::SDLK_F11                 => F11,
            sys::SDLK_F12                 => F12,
            sys::SDLK_PRINTSCREEN         => PrintScreen,
            sys::SDLK_SCROLLLOCK          => ScrollLock,
            sys::SDLK_PAUSE               => Pause,
            sys::SDLK_INSERT              => Insert,
            sys::SDLK_HOME                => Home,
            sys::SDLK_PAGEUP              => PageUp,
            sys::SDLK_END                 => End,
            sys::SDLK_PAGEDOWN            => PageDown,
            sys::SDLK_RIGHT               => Right,
            sys::SDLK_LEFT                => Left,
            sys::SDLK_DOWN                => Down,
            sys::SDLK_UP                  => Up,
            sys::SDLK_NUMLOCKCLEAR        => NumLockClear,
            sys::SDLK_KP_DIVIDE           => KpDivide,
            sys::SDLK_KP_MULTIPLY         => KpMultiply,
            sys::SDLK_KP_MINUS            => KpMinus,
            sys::SDLK_KP_PLUS             => KpPlus,
            sys::SDLK_KP_ENTER            => KpEnter,
            sys::SDLK_KP_1                => Kp1,
            sys::SDLK_KP_2                => Kp2,
            sys::SDLK_KP_3                => Kp3,
            sys::SDLK_KP_4                => Kp4,
            sys::SDLK_KP_5                => Kp5,
            sys::SDLK_KP_6                => Kp6,
            sys::SDLK_KP_7                => Kp7,
            sys::SDLK_KP_8                => Kp8,
            sys::SDLK_KP_9                => Kp9,
            sys::SDLK_KP_0                => Kp0,
            sys::SDLK_KP_PERIOD           => KpPeriod,
            sys::SDLK_APPLICATION         => Application,
            sys::SDLK_POWER               => Power,
            sys::SDLK_KP_EQUALS           => KpEquals,
            sys::SDLK_F13                 => F13,
            sys::SDLK_F14                 => F14,
            sys::SDLK_F15                 => F15,
            sys::SDLK_F16                 => F16,
            sys::SDLK_F17                 => F17,
            sys::SDLK_F18                 => F18,
            sys::SDLK_F19                 => F19,
            sys::SDLK_F20                 => F20,
            sys::SDLK_F21                 => F21,
            sys::SDLK_F22                 => F22,
            sys::SDLK_F23                 => F23,
            sys::SDLK_F24                 => F24,
            sys::SDLK_EXECUTE             => Execute,
            sys::SDLK_HELP                => Help,
            sys::SDLK_MENU                => Menu,
            sys::SDLK_SELECT              => Select,
            sys::SDLK_STOP                => Stop,
            sys::SDLK_AGAIN               => Again,
            sys::SDLK_UNDO                => Undo,
            sys::SDLK_CUT                 => Cut,
            sys::SDLK_COPY                => Copy,
            sys::SDLK_PASTE               => Paste,
            sys::SDLK_FIND                => Find,
            sys::SDLK_MUTE                => Mute,
            sys::SDLK_VOLUMEUP            => VolumeUp,
            sys::SDLK_VOLUMEDOWN          => VolumeDown,
            sys::SDLK_KP_COMMA            => KpComma,
            sys::SDLK_KP_EQUALSAS400      => KpEqualsAS400,
            sys::SDLK_ALTERASE            => AltErase,
            sys::SDLK_SYSREQ              => Sysreq,
            sys::SDLK_CANCEL              => Cancel,
            sys::SDLK_CLEAR               => Clear,
            sys::SDLK_PRIOR               => Prior,
            sys::SDLK_RETURN2             => Return2,
            sys::SDLK_SEPARATOR           => Separator,
            sys::SDLK_OUT                 => Out,
            sys::SDLK_OPER                => Oper,
            sys::SDLK_CLEARAGAIN          => ClearAgain,
            sys::SDLK_CRSEL               => CrSel,
            sys::SDLK_EXSEL               => ExSel,
            sys::SDLK_KP_00               => Kp00,
            sys::SDLK_KP_000              => Kp000,
            sys::SDLK_THOUSANDSSEPARATOR  => ThousandsSeparator,
            sys::SDLK_DECIMALSEPARATOR    => DecimalSeparator,
            sys::SDLK_CURRENCYUNIT        => CurrencyUnit,
            sys::SDLK_CURRENCYSUBUNIT     => CurrencySubUnit,
            sys::SDLK_KP_LEFTPAREN        => KpLeftParen,
            sys::SDLK_KP_RIGHTPAREN       => KpRightParen,
            sys::SDLK_KP_LEFTBRACE        => KpLeftBrace,
            sys::SDLK_KP_RIGHTBRACE       => KpRightBrace,
            sys::SDLK_KP_TAB              => KpTab,
            sys::SDLK_KP_BACKSPACE        => KpBackspace,
            sys::SDLK_KP_A                => KpA,
            sys::SDLK_KP_B                => KpB,
            sys::SDLK_KP_C                => KpC,
            sys::SDLK_KP_D                => KpD,
            sys::SDLK_KP_E                => KpE,
            sys::SDLK_KP_F                => KpF,
            sys::SDLK_KP_XOR              => KpXor,
            sys::SDLK_KP_POWER            => KpPower,
            sys::SDLK_KP_PERCENT          => KpPercent,
            sys::SDLK_KP_LESS             => KpLess,
            sys::SDLK_KP_GREATER          => KpGreater,
            sys::SDLK_KP_AMPERSAND        => KpAmpersand,
            sys::SDLK_KP_DBLAMPERSAND     => KpDblAmpersand,
            sys::SDLK_KP_VERTICALBAR      => KpVerticalBar,
            sys::SDLK_KP_DBLVERTICALBAR   => KpDblVerticalBar,
            sys::SDLK_KP_COLON            => KpColon,
            sys::SDLK_KP_HASH             => KpHash,
            sys::SDLK_KP_SPACE            => KpSpace,
            sys::SDLK_KP_AT               => KpAt,
            sys::SDLK_KP_EXCLAM           => KpExclam,
            sys::SDLK_KP_MEMSTORE         => KpMemStore,
            sys::SDLK_KP_MEMRECALL        => KpMemRecall,
            sys::SDLK_KP_MEMCLEAR         => KpMemClear,
            sys::SDLK_KP_MEMADD           => KpMemAdd,
            sys::SDLK_KP_MEMSUBTRACT      => KpMemSubtract,
            sys::SDLK_KP_MEMMULTIPLY      => KpMemMultiply,
            sys::SDLK_KP_MEMDIVIDE        => KpMemDivide,
            sys::SDLK_KP_PLUSMINUS        => KpPlusMinus,
            sys::SDLK_KP_CLEAR            => KpClear,
            sys::SDLK_KP_CLEARENTRY       => KpClearEntry,
            sys::SDLK_KP_BINARY           => KpBinary,
            sys::SDLK_KP_OCTAL            => KpOctal,
            sys::SDLK_KP_DECIMAL          => KpDecimal,
            sys::SDLK_KP_HEXADECIMAL      => KpHexadecimal,
            sys::SDLK_LCTRL               => LCtrl,
            sys::SDLK_LSHIFT              => LShift,
            sys::SDLK_LALT                => LAlt,
            sys::SDLK_LGUI                => LGui,
            sys::SDLK_RCTRL               => RCtrl,
            sys::SDLK_RSHIFT              => RShift,
            sys::SDLK_RALT                => RAlt,
            sys::SDLK_RGUI                => RGui,
            sys::SDLK_MODE                => Mode,
            sys::SDLK_AUDIONEXT           => AudioNext,
            sys::SDLK_AUDIOPREV           => AudioPrev,
            sys::SDLK_AUDIOSTOP           => AudioStop,
            sys::SDLK_AUDIOPLAY           => AudioPlay,
            sys::SDLK_AUDIOMUTE           => AudioMute,
            sys::SDLK_MEDIASELECT         => MediaSelect,
            sys::SDLK_WWW                 => Www,
            sys::SDLK_MAIL                => Mail,
            sys::SDLK_CALCULATOR          => Calculator,
            sys::SDLK_COMPUTER            => Computer,
            sys::SDLK_AC_SEARCH           => AcSearch,
            sys::SDLK_AC_HOME             => AcHome,
            sys::SDLK_AC_BACK             => AcBack,
            sys::SDLK_AC_FORWARD          => AcForward,
            sys::SDLK_AC_STOP             => AcStop,
            sys::SDLK_AC_REFRESH          => AcRefresh,
            sys::SDLK_AC_BOOKMARKS        => AcBookmarks,
            sys::SDLK_BRIGHTNESSDOWN      => BrightnessDown,
            sys::SDLK_BRIGHTNESSUP        => BrightnessUp,
            sys::SDLK_DISPLAYSWITCH       => DisplaySwitch,
            sys::SDLK_KBDILLUMTOGGLE      => KbdIllumToggle,
            sys::SDLK_KBDILLUMDOWN        => KbdIllumDown,
            sys::SDLK_KBDILLUMUP          => KbdIllumUp,
            sys::SDLK_EJECT               => Eject,
            sys::SDLK_SLEEP               => Sleep,
            _                             => return None
        })
    }
}

use std::fmt;

impl fmt::Display for Keycode {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.name())
    }
}

use crate::keyboard::Scancode;

impl Keycode {
    /// Gets the virtual key from a scancode. Returns None if there is no corresponding virtual key.
    pub fn from_scancode(scancode: Scancode) -> Option<Keycode> {
        const UNKNOWN: i32 = sys::SDLK_UNKNOWN as i32;
        unsafe {
            match sys::SDL_GetKeyFromScancode(transmute::<u32, sys::SDL_Scancode>(scancode as u32)) {
                UNKNOWN => None,
                keycode_id => Keycode::from_i32(keycode_id as i32)
            }
        }
    }

    pub fn from_name(name: &str) -> Option<Keycode> {
        const UNKNOWN: i32 = sys::SDLK_UNKNOWN as i32;
        unsafe {
            match CString::new(name) {
                Ok(name) => match sys::SDL_GetKeyFromName(name.as_ptr() as *const c_char) {
                    UNKNOWN => None,
                    keycode_id => Some(Keycode::from_i32(keycode_id as i32).unwrap())
                },
                // string contains a nul byte - it won't match anything.
                Err(_) => None
            }
        }
    }

    pub fn name(self) -> String {
        // The name string pointer's contents _might_ change, depending on the last call to SDL_GetKeyName.
        // Knowing this, we must always return a new string.
        unsafe {
            let buf = sys::SDL_GetKeyName(self as i32);
            CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned()
        }
    }
}
